﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Security;
using Storage;

namespace Groupware
{
    [Serializable]
    public class Message
    {

        public static byte HIGH;
        public static byte NORMAL;
        public static byte LOW;

        private int id;
        internal String sender;
        internal List<String> receiver;
        private String title;
        private String text;
        private bool priority;
        internal bool isRead;
        internal DateTime date;
        // true if sent, false if received
        internal bool sentORreceived;

        public int Id
        {
            get { return id; }
            internal set { id = value; }
        }

        public String Sender
        {
            get { return sender; }
        }

        public List<String> Receiver
        {
            get { return receiver; }
        }
        public String FirtReceiver
        {
            get { return receiver.First(); }
        }
        public String Title
        {
            get { return title; }
            set { title = value.Length > 128 ? value.Substring(0,128) : value; }
        }

        public String Text
        {
            get { return text; }
            set { text = value; }
        }

        public bool Priority
        {
            get { return priority; }
            set { priority = value; }
        }

        public bool IsRead
        {
            get { return isRead; }
        }

        public DateTime Date
        {
            get { return date; }
        }

        public Message()
        {
            receiver = new List<string>();
        }

        /// <summary>
        /// Marks as read this message
        /// </summary>
        public void MarkAsRead(TripLoaUser owner)
        {
            if (!sentORreceived)
            {
                Storage.EntityModels.TripLoaDataContext db = StorageManager.Instance.Entities;
                StorageManager manager = StorageManager.Instance;
                Storage.EntityModels.ReceivedMessages dbMsg = manager.GetReceivedMessageByUsernameAndMsgId(owner.UserName, this.id);
                dbMsg.IsRead = true;
                db.SubmitChanges();
            }
        }

        /// <summary>
        /// Send this message to the user whose username is provided
        /// </summary>
        /// <param name="sender">The user who is sending the message</param>
        /// <param name="receiver">The user this message is addressed to</param>
        /// <returns>A SendReport instance containing information about the result of the operation</returns>
        /// <exception cref="Groupware.Message.FieldsException">
        /// Thrown if one or both among Title and Text are set to null
        /// (see Message.FieldsException.Data property for further details; it contains [FieldName,Exception] pairs)
        /// </exception>
        public SendReport Send(TripLoaUser sender, string receiver)
        {
            Validate();
            //Validation passed
            SendReport report = new SendReport();
            try
            {
                if (sender.GetProfile().IsBannedBy(receiver))
                    report.AddHaveBanned(receiver);
                else
                {
                    List<string> receivers = new List<string>();
                    receivers.Add(receiver);
                    Storage.EntityModels.Message.CreateMessage(this.title, this.text, sender.UserID, receivers, this.priority, null, null);
                }
            }
            catch (Storage.Exceptions.UserNotFoundException)
            {
                report.AddNonexistent(receiver);
            }
            return report;
        }

        /// <summary>
        /// Send this message to the users whose usernames are provided
        /// </summary>
        /// <param name="sender">The user who is sending the message</param>
        /// <param name="receivers">The list of users this message is addressed to</param>
        /// <returns>A SendReport instance containing information about the result of the operation</returns>
        /// <exception cref="Groupware.Message.FieldsException">
        /// Thrown if one or both among Title and Text are set to null
        /// (see Message.FieldsException.Data property for further details; it contains [FieldName,Exception] pairs)
        /// </exception>
        public SendReport Send(TripLoaUser sender, List<string> receivers)
        {
            Validate();
            //Validation passed
            List<string> distinctReceivers = receivers.Distinct().ToList();
            List<string> toBeRemovedReceivers = new List<string>();
            SendReport report = new SendReport();
            foreach (string recipient in distinctReceivers)
            {
                try
                {
                    if (sender.GetProfile().IsBannedBy(recipient))
                    {
                        report.AddHaveBanned(recipient);
                        toBeRemovedReceivers.Add(recipient);
                    }
                }
                catch (Storage.Exceptions.UserNotFoundException)
                {
                    report.NonexistentRecipients.Add(recipient);
                    toBeRemovedReceivers.Add(recipient);
                }
            }
            distinctReceivers = distinctReceivers.Except(toBeRemovedReceivers).ToList();
            Storage.EntityModels.Message.CreateMessage(this.title, this.text, sender.UserID, distinctReceivers, this.priority, null, null);
            return report;
        }

        /// <summary>
        /// Delete this message
        /// </summary>
        public void Delete(TripLoaUser owner)
        {
            Storage.StorageManager db = Storage.StorageManager.Instance;
            if (sentORreceived) db.DeleteSentMessage(owner.UserName, this.id);
            else db.DeleteReceivedMessage(owner.UserName, this.id);
        }

        private void Validate()
        {
            IDictionary<String, Exception> errors = new Dictionary<string, Exception>();
            if (title == null)
                errors.Add("Title", new ArgumentNullException());
            if (text == null)
                errors.Add("Text", new ArgumentNullException());
            if (errors.Count != 0)
            {
                FieldsValidationException e = new FieldsValidationException("Wrong field(s)");
                foreach (KeyValuePair<String, Exception> err in errors)
                    e.Data.Add(err.Key, err.Value);
                throw e;
            }
        }
        
    }

    /// <summary>
    /// This class provides informations about a send operation results.
    /// 
    /// If the operation is successfully completed, the Succeded property is TRUE and the other properties are meaningless.
    /// 
    /// If the operation could not be completely performed, the Succeded property is FALSE and the NonexistentRecipients
    /// property contains all the nonexisting usernames, whereas the HaveBannedSender property contains all the usernames
    /// whose the corresponding users have banned the sender user. In such a situation for all the recipients contained
    /// in the original recipient's list but those contained in the union of NonexistentRecipients and HaveBannedSender,
    /// the message delivery has been successfully completed.
    /// </summary>
    public class SendReport
    {
        private List<string> nonexistentRecipients;
        private List<string> haveBannedSender;

        /// <summary>
        /// TRUE if the send operation was successfully completed
        /// FALSE if errors occurred during the operation (see NonexistingRecipients and HaveBannedSender properties for further details)
        /// </summary>
        public bool Succeded
        {
            get { return (nonexistentRecipients.Count == 0); }
        }

        /// <summary>
        /// Contains all the nonexisting recipients' usernames
        /// </summary>
        public List<string> NonexistentRecipients
        {
            get { return nonexistentRecipients; }
            internal set { this.nonexistentRecipients = value; }
        }
        
        /// <summary>
        /// Contains all the recipients' usernames whose corresponding user have banned the sender
        /// </summary>
        public List<string> HaveBannedSender { get { return haveBannedSender; } }

        internal SendReport()
        {
            nonexistentRecipients = new List<string>();
            haveBannedSender = new List<string>();
        }

        internal void AddNonexistent(string recipientUsername)
        {
            this.nonexistentRecipients.Add(recipientUsername);
        }

        internal void AddHaveBanned(string recipientUsername)
        {
            this.haveBannedSender.Add(recipientUsername);
        }

    }

    /// <summary>
    /// This class provides informations on a Message validation failure.
    /// The Message property contains a pair [Fieldname,Exception] for each field of the validated Groupware.Message instance
    /// In order to know which field(s) didn't pass the validation, check the Data property.
    /// </summary>
    public class FieldsValidationException : Exception
    {
        public FieldsValidationException(string message) : base(message) { }
        public override string Message
        {
            get
            {
                StringBuilder sb = new StringBuilder(base.Message);
                foreach (DictionaryEntry err in Data)
                {
                    sb.Append("\n");
                    sb.Append(err.Key);
                    sb.Append(": ");
                    sb.Append(err.Value);
                }
                return sb.ToString();
            }
        }
    }

}
